// Copyright 2023 Srhino Co., Ltd. All rights reserved.

package utils

import (
	"context"
	"encoding/json"
	"net/http"
	"time"
)

type RestServer interface {
	RegisterHandler(string, http.Handler)
	RegisterHandlerFunc(string, http.HandlerFunc)

	Start() error
	ReStart(config *RestServerConfig) error
	Shutdown() error
}

type RestServerConfig struct {
	Name    string
	Address string
}

type restServer struct {
	ctx context.Context

	config *RestServerConfig
	mux    *http.ServeMux
	server *http.Server
}

type ResponseResult struct {
	Code int         `json:"code"`
	Err  string      `json:"err"`
	Data interface{} `json:"data,omitempty"`
	Msg  string      `json:"msg,omitempty"`
}

func NewEmptyResponseResult() *ResponseResult {
	return &ResponseResult{
		Code: 200,
		Err:  "",
		Data: struct{}{},
		Msg:  "success",
	}
}

func (r *ResponseResult) Bytes() (bs []byte) {
	bs, _ = json.Marshal(r)
	return
}

var (
	_             RestServer = (*restServer)(nil)
	newHttpServer            = func() *http.Server {
		return &http.Server{
			ReadTimeout: time.Second * 5,
			IdleTimeout: time.Second * 5,
		}
	}
)

func NewRestServer(ctx context.Context, config *RestServerConfig) RestServer {
	return &restServer{
		ctx:    ctx,
		config: config,
		mux:    http.NewServeMux(),
		server: newHttpServer(),
	}
}

func (s *restServer) RegisterHandler(pattern string, handler http.Handler) {
	s.mux.Handle(pattern, handler)
}
func (s *restServer) RegisterHandlerFunc(pattern string, handlerFunc http.HandlerFunc) {
	s.mux.HandleFunc(pattern, handlerFunc)
}

func (s *restServer) Start() error {
	s.server.Addr = s.config.Address
	s.server.Handler = s.mux

	go func() {
		Logger.Infof("Rest server(%s) listening on %s", s.config.Name, s.config.Address)
		if err := s.server.ListenAndServe(); err != nil {
			Logger.Error(err)
		}
	}()

	return nil
}

func (s *restServer) ReStart(config *RestServerConfig) error {
	err := s.Shutdown()
	if err != nil {
		return err
	}

	s.config = config
	s.server = newHttpServer()
	return s.Start()
}

func (s *restServer) Shutdown() error {
	return s.server.Shutdown(s.ctx)
}
